#ifndef _GRAPHICS_H_
#define _GRAPHICS_H_

#include "../sleipner_config.h"
#include <string>
#include <vector>
#include "math/vec2.h"
#include "math/vec3.h"
#include "math/viewport.h"
#include "color.h"

namespace math
{
	class	Mat4x4;
}

class Font;
class Image;
class ImageCache;
class AVI;
/***
 * @brief: Abstract base class different graphics classes
 */

class Graphics
{
public:
	static int s_PixelOffsetX;
	static int s_PixelOffsetY;

	enum	GRAPHICS_CLASS
	{
		GRAPHICS_CLASS_DX6,
		GRAPHICS_CLASS_DX7,
		GRAPHICS_CLASS_DX8,
		GRAPHICS_CLASS_DX9,
		GRAPHICS_CLASS_OGL,
		GRAPHICS_CLASS_WII			
	};
	enum	RENDER_STATE
	{	
		RS_ALPHA,
		RS_ALPHA_TEST,			//! cull fragments under a certain alpha value
		RS_ALPHA_REF,			//! used if alpha testing
		RS_LIGHTING,
		RS_ZWRITE,
		RS_ZREAD,
		RS_SRC_BLEND,
		RS_DST_BLEND,
		RS_CULL_MODE,
		// do we try for everything, or just add lazily
		RENDER_STATE_C,
		RENDER_STATE_FORCE = 0x7fffffff
	};
	enum	RENDERSTATE_VAL
	{
		/// cull modes
		RV_CULL_CW,				//! Clockwise culling
		RV_CULL_CCW,			//! Counter-Clockwise culling
		RV_CULL_NONE,			//! render both sides

		/// values for setting up src/dst blend
		RV_BLEND_ONE,		
		RV_BLEND_ZERO,		
		RV_BLEND_SRC,		
		RV_BLEND_INVSRC,		
		RV_BLEND_SRC_ALPHA,		//! Normal src alpha
		RV_BLEND_INVSRC_ALPHA,	//! Normal dst alpha
		RV_BLEND_DEST,		
		RV_BLEND_INVDEST,		
		RV_BLEND_DEST_ALPHA,		//! Normal src alpha
		RV_BLEND_INVDEST_ALPHA,	//! Normal dst alpha
		RV_COUNT,
		RENDERSTATE_VAL_FORCE = 0x7fffffff
	};

	enum	BLEND_OP
	{
		// blend values
		BLEND_MODULATE,
		BLEND_ADD,
		BLEND_TEXTUREALPHA,
		BLEND_SELECTARG1,
		BLEND_SELECTARG2,
		BLEND_DISABLE,

		// stage values
		SV_DIFFUSE,
		SV_CURRENT,
		SV_TEXTURE,
		SV_CONSTANT,		//! T-Factor in DX, glColor3x in OpenGL

		// Texture states
		TEX_LINEAR,
		TEX_NONE,

		BLEND_OP_COUNT,
		BLEND_OP_FORCE = 0x7fffffff
	};
	enum	STAGE_TYPE
	{
		COLOR_ARG1,
		COLOR_ARG2,
		ALPHA_ARG1,
		ALPHA_ARG2,
		COLOR_OP,
		ALPHA_OP,
		MIP_FILTER,
		STAGE_TYPE_COUNT,
		STAGE_TYPE_FORCE = 0x7fffffff
	};
	enum	TRANSFORM_STAGE
	{
		TS_WORLD,
		TS_VIEW,
		TS_PROJECTION,
		TS_TEX0,
		TS_TEX1,
		TS_TEX2,
		TS_TEX3,
		TS_TEX4,
		TS_TEX5,
		TS_TEX6,
		TS_TEX7,
		TS_COUNT,
		TS_FORCE = 0x7fffffff
	};
	enum	PRIMITIVE_TYPE
	{
		PT_POINT_LIST,
		PT_LINE_LIST,
		PT_LINE_STRIP,
		PT_TRI_LIST,
		PT_TRI_STRIP,
		PT_TRI_FAN,
		PT_COUNT,
		PT_FORCE = 0x7fffffff
	};
#ifdef _PLATFORM_WII
	typedef float			POS_TYPE;
#else
	typedef float			POS_TYPE;
#endif
	struct	Vertex2D
	{
		POS_TYPE		x, y, z, w;
		COLOR			color;     
		float          u, v; 
		inline void		FillRaw(int _x, int _y, COLOR _c, float _u, float _v)
		{
			x = (POS_TYPE)_x ;
			y = (POS_TYPE)_y ;
			z = (POS_TYPE)0;
			w = (POS_TYPE)1;
			color = _c;
			u = _u ;
			v = _v ;

		};
		inline void		Fill(float _x, float _y, COLOR	 _color, float tu, float tv )
		{
			x = (POS_TYPE)(_x + s_PixelOffsetX);
			y = (POS_TYPE)(_y + s_PixelOffsetY);
			z = (POS_TYPE)0;
			w = (POS_TYPE)1;
			color = _color;
			u = tu;
			v = tv;
			
		};
		inline void		Fill(float _x, float _y, float _z, float _w, COLOR _color, float tu, float tv )
		{
			x = _x + (POS_TYPE)s_PixelOffsetX;
			y = _y + (POS_TYPE)s_PixelOffsetY;
			z = _z;
			w = _w;
			color = _color;
			u = tu;
			v = tv ;

		};
	};
	struct	FatVertex2D
	{
		float			x, y, z, w;
		COLOR			color;     
		float           u, v; 
		float           u2, v2; 
		inline void		Fill(float _x, float _y, COLOR	 _color, float tu, float tv, float tu2, float tv2  )
		{
			x = _x + s_PixelOffsetX;
			y = _y + s_PixelOffsetY;
			z = 0.0f;
			w = 1.0f;
			color = _color;
			u = tu ;
			v = tv ;
			u2 = tu2;
			v2 = tv2;
		};
		inline void		Fill(float _x, float _y, float _z, float _w, COLOR _color, float tu, float tv, float tu2, float tv2   )
		{
			x = _x + s_PixelOffsetX;
			y = _y + s_PixelOffsetY;
			z = _z;
			w = _w;
			color = _color;
			u = tu;
			v = tv;
			u2 = tu2;
			v2 = tv2;
		};
	};
	struct	Vertex3D
	{
		float		x, y, z;
		COLOR		color;
		float		u, v;
		inline void	Fill(float _x, float _y, float _z, COLOR _color, float _u, float _v)
		{
			x = _x;
			y = _y;
			z = _z;
			color = _color;
			u = _u;
			v = _v;
		};
		inline void	Fill(const math::Vec3& Pos, COLOR _color, float _u, float _v)
		{
			x = Pos.x;
			y = Pos.y;
			z = Pos.z;
			color = _color;
			u = _u;
			v = _v;
		};
	};

	// these can be borrowed for quick drawing.
	static const int			MAX_TEMP_VERT = 20000;
	static const int			MAX_TEMP_INDEX = MAX_TEMP_VERT * 6;	
	static Graphics::Vertex2D*	GetTempVert();
	static unsigned short*		GetTempIdx();



					Graphics();
	virtual			~Graphics();

	// startup and maintainance
	virtual	bool	Init() = 0;
	virtual bool	InitWindow(unsigned int Width, unsigned int Height, bool Fullscreen, bool EnableDebug = false) = 0;
	virtual void	RegisterFormats() = 0;
	virtual void*	GetDeviceDriver() const { return NULL;	};
	virtual void*	GetFlashDriver()  = 0;

	virtual void	GetBestWideScreenRes(unsigned int& Width, unsigned int& Height, bool bGetForBackbuffer = true) = 0;

	virtual	bool	Shutdown() = 0;
	virtual	bool	ShutdownWindow() = 0;
	virtual	bool	Resize(unsigned int Width, unsigned int Height);
	virtual bool	ResetWindow(unsigned int Width, unsigned int Height, bool Fullscreen) = 0;

	// begin and end scene
	virtual bool	BeginScene() = 0;
	virtual void	EndScene() = 0;
	virtual void	Clear(COLOR c, float Depth = 1.0f, int Stencil = 0, bool ClearRGB = true, bool ClearDepth= true, bool ClearStencil= true) = 0;
	virtual bool	Present() = 0;


	// initialize internals for usual 2D and 3D options
	virtual void	Start2D() = 0;
	virtual void	Start3D() = 0;

	// texture stage stuff
	virtual void	SetTexStage(unsigned int TexStage, STAGE_TYPE type, BLEND_OP Op) = 0;
	virtual void	SetRenderState(RENDER_STATE r, unsigned int val) = 0;
	virtual void	SetTexture(unsigned int Stage, Image* pImg) = 0;

	// transforms
	virtual void	SetTransform(TRANSFORM_STAGE ts, const math::Mat4x4& mat) = 0;
	virtual void	SetViewport(const math::Viewport& vp) = 0;

	// simplified draws.
	// 2D:
	virtual void	DrawVertex2D(Vertex2D*	VertA, unsigned int VertC, PRIMITIVE_TYPE Type = PT_TRI_LIST) = 0;
	virtual void	DrawIndexVertex2D(Vertex2D*	VertA, unsigned int VertC, unsigned short* IndexA, unsigned int IndexC, PRIMITIVE_TYPE Type = PT_TRI_LIST) = 0;

	virtual void	DrawFatIndexVertex2D(FatVertex2D*	VertA, unsigned int VertC, unsigned short* IndexA, unsigned int IndexC, PRIMITIVE_TYPE Type = PT_TRI_LIST) = 0;
	virtual void	DrawVertex3D(Vertex3D*	VertA, unsigned int VertC, PRIMITIVE_TYPE Type = PT_TRI_LIST) = 0;
	virtual void	DrawIndexVertex3D(Vertex3D*	VertA, unsigned int VertC, unsigned short* IndexA, unsigned int IndexC, PRIMITIVE_TYPE Type = PT_TRI_LIST) = 0;

	// accessors
    unsigned short   GetScreenWidth() const { return m_ScreenWidth; };
    unsigned short   GetScreenHeight() const { return m_ScreenHeight; };
	static Graphics* GetGraphics() { return s_pGraphics; };

	// images
	virtual Image*	LoadImageFromFile(const std::string& Name) = 0;
	virtual Image*	LoadImageFromMemory(unsigned char* pData, unsigned int	Size) = 0;
	virtual Image*	CreateDynamicImage(unsigned short Width = 256, unsigned short Height= 256, unsigned short Depth=32) = 0;
	virtual Font*	CreateTTFFont(const std::string& Name) = 0;
	virtual AVI*	LoadMovie(const std::string& Name) = 0;
	virtual void	DestroyMovie(AVI* pAVI) = 0;
	virtual ImageCache*		CreateCache(int Width, int Height, int Depth) = 0;
	
	void			TakeScreenShot(const std::string& file_name, bool Minimize = false);

	// force a splash-screen
	//virtual void	ForceSplash(Image* pImg);

	GRAPHICS_CLASS		GetGraphicsClass() const { return m_GraphicsClass; };	
	virtual void		SetFilter2D(bool b) { m_Filter2D = b; };
	bool				GetFilter2D() const { return m_Filter2D; };
	unsigned int		GetFrame() const { return m_FrameID; };
	static void			SHUTDOWN();

	struct				ImageLayer
	{
		Image*			pImage;
		int				x, y;
	};
	virtual Image*		CreateComposite(int	width, int height, int depth, const std::vector<ImageLayer>& Layers) = 0;
	void				RenderBorders();

	virtual void		Flush();
	virtual void		Recreate();
	void				AddImageCache(ImageCache* pCache);
	void				DropImageCache(ImageCache* pCache);

	void				SetOriginalPixelOffset(int x, int y);
	void				GetOriginalPixelOffset(int &x, int& y);
	void				EnableMipmap(bool b) { m_EnableMipmaps = b; };
	bool				IsMipmapEnabled() const { return m_EnableMipmaps; };

	int					GetTotalPolyC() const { return m_PolygonC; };
	int					GetTotalBatchC() const { return m_BatchC; };
	void				SetDebugTexture(Image*	pImg) { m_pForceTexture = pImg; };

	void				SetupAdditiveAlpha(bool b);		// helper function for additive alpha
protected:

	void				SetGraphicsClass(GRAPHICS_CLASS GraphicClass) { m_GraphicsClass = GraphicClass; }

	unsigned short      m_ScreenWidth;              /// screen width
    unsigned short      m_ScreenHeight;             /// screen height
	int					m_WindowX, m_WindowY;		// window position
	static	Graphics*	s_pGraphics;
	bool				m_IsWindowed;
	bool				m_Filter2D;
	unsigned int		m_FrameID;

	int					m_OrgPixelX, m_OrgPixelY;
	bool				m_EnableMipmaps;
	/// screenshot info
	std::string			m_ScreenshotFile;
	bool				m_ScreenshotMinimize;

	// debugging info
	int					m_PolygonC;
	int					m_BatchC;
	Image*				m_pForceTexture;

	GRAPHICS_CLASS		m_GraphicsClass;

	std::vector<ImageCache*>	m_ImageCaches;
	std::vector<AVI*>			m_ActiveMovies;
};

Graphics* GetGraphics();


#endif // _GRAPHICS_H_